//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.IO.Packaging;
using System.Xml;
using OpenLS.Core.Properties;

namespace OpenLS.Core.Serialization
{
    public partial class ReadContext : IDisposable
    {
        #region Delegates

        public delegate T ElementFactory<T>();

        #endregion

        [ThreadStatic] private static EnumConverter _enumConverter;
        private string _childNamespace;
        private Dictionary<object, object> _data;
        private bool _duplicatedData;
        private bool _forceNamespace;
        private string _name;
        private string _namespaceUri;
        private PackagePart _part;
        internal XmlReader _reader;
        private ReadState _state;

        public ReadContext(XmlReader reader, PackagePart part)
        {
            _reader = reader;
            _part = part;
        }

        public ReadContext(XmlReader reader, PackagePart part, string namespaceUri)
        {
            _reader = reader;
            _part = part;
            _namespaceUri = namespaceUri;
        }

        public ReadContext(XmlReader reader, PackagePart part, bool needsClose)
        {
            _reader = reader;
            _part = part;
            _state = ReadState.Before;
        }

        public ReadContext(XmlReader reader, PackagePart part, string uri, bool forceNamenpace)
            : this(reader, part, uri)
        {
            _forceNamespace = forceNamenpace;
        }

        public bool IsValid
        {
            get
            {
                var ctx = GetData<BackgroundWorker>();
                return ctx == null || !ctx.CancellationPending;
            }
        }

        ///<summary>
        /// Get the local name of the underlying XML reader
        ///</summary>
        public string ReaderLocalName
        {
            get
            {
                PassAttributes();
                _reader.MoveToContent();
                if (_reader.NodeType == XmlNodeType.EndElement)
                    return null;
                if (_state == ReadState.AfterAttributesEmpty)
                    return null;
                return Reader.LocalName;
            }
        }

        public string NamespaceUri
        {
            get { return _namespaceUri; }
        }

        public XmlNamespace Namespace
        {
            get { return new XmlNamespace(null, _namespaceUri, _forceNamespace); }
        }

        public XmlReader Reader
        {
            get { return _reader; }
            set { _reader = value; }
        }

        public PackagePart Part
        {
            get { return _part; }
            set { _part = value; }
        }

        public bool ForceNamespace
        {
            get { return _forceNamespace; }
        }

        private static EnumConverter enumConverter
        {
            get
            {
                if (_enumConverter == null)
                    _enumConverter = new EnumConverter();
                return _enumConverter;
            }
        }

        private string childNamespace
        {
            get
            {
                if (_childNamespace != null)
                    return _childNamespace;
                return _namespaceUri;
            }
        }

        public OfficeXmlAttribute this[string attributeName]
        {
            get
            {
                string name = GetString(attributeName);
                if (name == null)
                    return null;
                return new OfficeXmlAttribute(name);
            }
        }

        #region IDisposable Members

        public virtual void Dispose()
        {
            if (!IsValid)
                return;
            switch (_state)
            {
                case ReadState.None:
                    return;
                case ReadState.Before:
                    bool isEmpty = _reader.IsEmptyElement;
                    if (isEmpty)
                        _reader.Read();
                    else
                    {
                        _reader.ReadStartElement();
                        _reader.ReadEndElement();
                    }
                    _state = ReadState.Disposed;
                    _reader.MoveToContent();
                    return;
                case ReadState.AfterAttributes:
                    _reader.ReadEndElement();
                    _state = ReadState.Disposed;
                    _reader.MoveToContent();
                    return;
                case ReadState.AfterAttributesEmpty:
                    if (_namespaceUri != null)
                        _reader.ReadStartElement(_name, _namespaceUri);
                    else
                        _reader.ReadStartElement(_name);
                    _state = ReadState.Disposed;
                    _reader.MoveToContent();
                    return;

                default:
                    throw new NotSupportedException();
            }
        }

        #endregion

        public static RelatedReadContext Open(PackagePart part, PackageRelationship rel, string namespaceUri)
        {
            Stream stream = part.GetStream();
            XmlReader reader = CreateXmlReader(stream);
            return new RelatedReadContext(reader, part, namespaceUri, stream, rel);
        }

        private static XmlReader CreateXmlReader(Stream stream)
        {
            return XmlReader.Create(stream);
            //return new XmlTextReader(stream);
        }

        public RelatedReadContext OpenRelatedContext(PackageRelationship relationship, string namespaceUri)
        {
            PackagePart newPart =
                Part.Package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
            RelatedReadContext result = Open(newPart, relationship, namespaceUri);
            result.CopyDataFrom(this);
            return result;
        }

        public PackagePart GetPart(PackageRelationship relationship)
        {
            PackagePart result =
                _part.Package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
            return result;
        }

        public RelatedReadContext OpenRelatedContext(string relationshipId, string namespaceUri)
        {
            return OpenRelatedContext(Part.GetRelationship(relationshipId), namespaceUri);
        }

        public Stream OpenRelatedStream(string relationshipId)
        {
            PackageRelationship relationship = Part.GetRelationship(relationshipId);
            PackagePart newPart =
                _part.Package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
            return newPart.GetStream();
        }

        public RelatedReadContext GetRelatedContext(string relationshipType, string namespaceUri)
        {
            foreach (PackageRelationship rel in Part.GetRelationshipsByType(relationshipType))
            {
                RelatedReadContext result = OpenRelatedContext(rel, namespaceUri);
                result._forceNamespace = _forceNamespace;
                return result;
            }
            return null;
        }

        public IEnumerable<RelatedReadContext> GetRelatedContexts(string relationshipType, string namespaceUri)
        {
            foreach (PackageRelationship rel in Part.GetRelationshipsByType(relationshipType))
            {
                RelatedReadContext result = OpenRelatedContext(rel, namespaceUri);
                result._forceNamespace = _forceNamespace;
                yield return result;
            }
        }

        public void CopyDataFrom(ReadContext context)
        {
            if (context._data == null)
                return;
            _data = new Dictionary<object, object>(context._data);
        }

        public void AddData(object key, object value)
        {
            if (!_duplicatedData)
            {
                _data = _data == null ? new Dictionary<object, object>() : new Dictionary<object, object>(_data);
                _duplicatedData = true;
            }
            _data.Add(key, value);
        }

        public void AddData<T>(T value)
        {
            AddData(typeof (T), value);
        }

        public object GetData(object key)
        {
            if (_data == null)
                return null;
            object result;
            if (_data.TryGetValue(key, out result))
                return result;
            return null;
        }

        public T GetData<T>()
        {
            return (T) GetData(typeof (T));
        }

        public ReadContext Read(string name)
        {
            var result = new ReadContext(_reader, _part, true);
            result.copyFrom(this);
            result._namespaceUri = childNamespace;
            result._name = name;
            PassAttributes();
            moveToContent();
            result._data = _data;
            if (_reader.LocalName == AlternateContent.xmlName &&
                _reader.NamespaceURI == OfficeConstants.markupCompatibilityUri)
            {
                throw new AlternateContentException();
            }
            return result;
        }
        internal ReadContext ReadFromAlternateContent(XmlQualifiedName name)
        {
            var result = new ReadContext(_reader, _part, true);
            result.copyFrom(this);
            result._namespaceUri = name.Namespace.NamespaceUri;
            result._name = name.LocalName;
            PassAttributes();
            moveToContent();
            result._data = _data;
            return result;
        }
        private void copyFrom(ReadContext c)
        {
            _namespaceUri = c._namespaceUri;
            _name = c._name;
            _forceNamespace = c._forceNamespace;
        }

        public ReadContext Read(XmlQualifiedName qualifiedName)
        {
            return read(qualifiedName.LocalName, qualifiedName.Namespace);
        }

        private ReadContext read(string name, XmlNamespace ns)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentException("_name");
            var result = new ReadContext(_reader, _part, true);
            result.copyFrom(this);
            result._namespaceUri = ns.NamespaceUri;
            result._name = name;
            result._data = _data;
            result._forceNamespace = ns.QualifiedAttributes;
            return result;
        }

        public string GetString(string attributeName)
        {
            moveToContent();
            if (_forceNamespace)
                return _reader[attributeName, NamespaceUri];
            string result = _reader[attributeName];
            if (result == null)
                throw new NotSupportedException(Resources.MissingAttribute + attributeName);
            return result;
        }

        public string GetString(XmlQualifiedName qualifiedName)
        {
            return getString(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri);
        }

        private void moveToContent()
        {
            _reader.MoveToContent();
        }

        public double ReadElementDoubleAttribute(string elementName)
        {
            return ReadElementDoubleAttribute(elementName, "val");
        }

        public int GetOptionalUIntegerAsInteger(string attributeName, int defaultValue)
        {
            return (int) GetOptionalUInteger(attributeName, (uint) defaultValue);
        }

        public int? GetOptionalUIntegerAsInteger(string p)
        {
            uint? result = GetOptionalUInteger(p);
            if (result == null)
                return null;
            return (int) result.Value;
        }


        public int ReadElementUIntegerAttributeAsInteger(string elementName, string attributeName)
        {
            return (int) ReadElementUIntegerAttribute(elementName, attributeName);
        }

        public long GetInteger64(string attributeName)
        {
            return long.Parse(GetString(attributeName));
        }


        public T GetEnum<T>(string attributeName)
        {
            return enumConverter.GetValue<T>(GetString(attributeName));
        }

        public T GetEnum<T>(XmlQualifiedName qualifiedName) where T : struct
        {
            return getEnumWithNamespace<T>(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri);
        }

        private T getEnumWithNamespace<T>(string attributeName, string namespaceUri) where T : struct
        {
            return enumConverter.GetValue<T>(_reader.GetAttribute(attributeName, namespaceUri));
        }

        public T? GetOptionalEnum<T>(XmlQualifiedName qualifiedName) where T : struct

        {
            return getOptionalEnumWithNamespace<T>(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri);
        }

        private T? getOptionalEnumWithNamespace<T>(string attributeName, string namespaceUri) where T : struct
        {
            string s = _reader.GetAttribute(attributeName, namespaceUri);
            if (s == null)
                return null;
            return enumConverter.GetValue<T>(s);
        }

        public bool GetOptionalBoolean(string attributeName, bool defaultValue)
        {
            bool? b = GetOptionalBoolean(attributeName);
            if (b != null)
                return b.Value;
            return defaultValue;
        }

        public bool? GetOptionalBoolean(string attributeName)
        {
            string s = GetOptionalString(attributeName);
            if (s == null)
                return null;
            return GetBoolean(attributeName);
        }

        public bool GetOptionalBoolean(XmlQualifiedName qualifiedName, bool defaultValue)
        {
            return getOptionalBooleanWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri,
                                                   defaultValue);
        }

        private bool getOptionalBooleanWithNamespace(string attributeName, string namespaceUri, bool defaultValue)
        {
            string s = _reader[attributeName, namespaceUri];
            if (s == null)
                return defaultValue;
            return stringToBoolean(s);
        }

        public bool? GetOptionalBoolean(XmlQualifiedName qualifiedName)
        {
            return getOptionalBooleanWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri);
        }

        private bool? getOptionalBooleanWithNamespace(string attributeName, string namespaceUri)
        {
            string s = _reader[attributeName, namespaceUri];
            if (s == null)
                return null;
            return stringToBoolean(s);
        }

        public string ReadOptionalElementString(string elementName)
        {
            passAttributesAndMoveToContent();

            if (ReaderLocalName != elementName)
                return null;
            return ReadElementString(elementName);
        }
        public string ReadOptionalElementString(XmlQualifiedName elementName)
        {
            passAttributesAndMoveToContent();

            if (ReaderLocalName != elementName.LocalName)
                return null;
            return ReadElementString(elementName);
        }

        private void passAttributesAndMoveToContent()
        {
            PassAttributes();
            moveToContent();
        }

        public string ReadElementString(string elementName)
        {
            PassAttributes();
            if (_namespaceUri != null)
                return _reader.ReadElementString(elementName, _namespaceUri);
            return _reader.ReadElementString(elementName);
        }
        public string ReadElementString(XmlQualifiedName elementName)
        {
            PassAttributes();
            //if (_namespaceUri != null)
                return _reader.ReadElementString(elementName.LocalName, elementName.Namespace.NamespaceUri);
            //return _reader.ReadElementString(elementName);
        }

        public T ReadElementEnum<T>(string elementName) where T : struct
        {
            return enumConverter.GetValue<T>(ReadElementString(elementName));
        }

        public T ReadElementEnum<T>(string elementName, string namespaceUri) where T : struct
        {
            return enumConverter.GetValue<T>(ReadElementString(elementName, namespaceUri));
        }

        public string ReadElementString(string elementName, string namespaceUri)
        {
            PassAttributes();
            if (namespaceUri != null)
                return _reader.ReadElementString(elementName, namespaceUri);
            return _reader.ReadElementString(elementName);
        }

        public void PassAttributes()
        {
            if (_state == ReadState.Before)
            {
                _reader.MoveToContent();
                if (_reader.IsEmptyElement)
                    _state = ReadState.AfterAttributesEmpty;
                else
                {
                    if (_namespaceUri != null)
                        _reader.ReadStartElement(_name, _namespaceUri);
                    else
                        _reader.ReadStartElement(_name);
                    _state = ReadState.AfterAttributes;
                }
            }
        }

        public bool GetBoolean(string attributeName)
        {
            string s = _reader.GetAttribute(attributeName);
            return stringToBoolean(s);
        }

        private static bool stringToBoolean(string s)
        {
            switch (s)
            {
                case "0":
                case "false":
                case "f":
                case "off":
                    return false;
                case null:
                case "1":
                case "true":
                case "t":
                case "on":
                    return true;
                default:
                    throw new NotSupportedException();
            }
        }

        public int GetOptionalInteger(XmlQualifiedName qualifiedName, int defaultValue)
        {
            return getOptionalIntegerWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri,
                                                   defaultValue);
        }

        public int GetOptionalInteger(string attributeName, int defaultValue)
        {
            int? b = GetOptionalInteger(attributeName);
            if (b != null)
                return b.Value;
            return defaultValue;
        }

        public int? GetOptionalInteger(XmlQualifiedName qualifiedName)

        {
            return getOptionalIntegerWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri);
        }

        private int? getOptionalIntegerWithNamespace(string attributeName, string namespaceUri)
        {
            string s = _reader[attributeName, NamespaceUri];
            if (s != null)
                return int.Parse(s);
            return null;
        }

        public int GetOptionalIntegerWithNamespace(XmlQualifiedName qualifiedName, int defaultValue)
        {
            return getOptionalIntegerWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri,
                                                   defaultValue);
        }

        private int getOptionalIntegerWithNamespace(string attributeName, string namespaceUri, int defaultValue)
        {
            int? b = getOptionalIntegerWithNamespace(attributeName, namespaceUri);
            if (b != null)
                return b.Value;
            return defaultValue;
        }

        public long GetOptionalInteger64(string attributeName, long defaultValue)
        {
            long? b = GetOptionalInteger64(attributeName);
            if (b != null)
                return b.Value;
            return defaultValue;
        }

        public T GetOptionalEnum<T>(XmlQualifiedName qualifiedName, T defaultValue) where T : struct
        {
            return getOptionalEnumWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri,
                                                defaultValue);
        }

        public T GetOptionalEnum<T>(string attributeName, T defaultValue) where T : struct
        {
            T? result = GetOptionalEnum<T>(attributeName);
            if (result == null)
                return defaultValue;
            return result.Value;
        }

        public string GetOptionalString(string attributeName)
        {
            moveToContent();
            if (_forceNamespace)
                return _reader[attributeName, NamespaceUri];
            return _reader[attributeName];
        }

        private string getOptionalStringWithNamespace(string name, string namespaceUri)
        {
            return _reader[name, namespaceUri];
        }

        public string GetOptionalString(XmlQualifiedName name)
        {
            return getOptionalStringWithNamespace(name.LocalName, name.Namespace.NamespaceUri);
        }


        public T ReadOptionalElementAs<T>(string name) where T : IOfficeXmlSerializableAs, new()
        {
            PassAttributes();
            if (ReaderLocalName != name)
                return default(T);
            //\\ using (ReadContext c = this)
            {
                var t = new T();
                t.ReadXmlAs(name, this);
                return t;
            }
        }
        /// <summary>
        /// Read the Xml element if it matches elementName and return a deserialiazed instance of T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="elementName"></param>
        /// <returns></returns>
        public T ReadOptionalElement<T>(string elementName) where T : IOfficeXmlSerializable, new()
        {
            string s = ReaderLocalName;
            if (s == AlternateContent.xmlName && _reader.NamespaceURI == OfficeConstants.markupCompatibilityUri)
            {
                var alternateContent = new AlternateContent<T>();
                alternateContent.ReadOptional(elementName, this); //\\
                return alternateContent.Item;
            }
            if (s != elementName)
                return default(T);
            var t = new T();
            t.ReadXml(this);
            return t;
        }

        public T ReadOptionalElement<T>(string elementName, ElementFactory<T> factory) where T : IOfficeXmlSerializable
        {
            if (ReaderLocalName != elementName)
                return default(T);
            {
                T t = factory();
                t.ReadXml(this);
                return t;
            }
        }


        public List<T> ReadOptionalListElement<T>(string names, string elementName)
            where T : class, IOfficeXmlSerializable, new()
        {
            PassAttributes();
            if (ReaderLocalName != names)
                return new List<T>();
            using (ReadContext c = Read(names))
            {
                var result = new List<T>();
                var t = c.ReadOptionalElement<T>(elementName);
                while (t != null)
                {
                    result.Add(t);
                    t = c.
                        ReadOptionalElement<T>(elementName);
                }
                return result;
            }
        }
        public List<T> ReadOptionalListElement<T>(string names, string elementName, ElementFactory<T> factory )
            where T : class, IOfficeXmlSerializable
        {
            PassAttributes();
            if (ReaderLocalName != names)
                return new List<T>();
            using (ReadContext c = Read(names))
            {
                var result = new List<T>();
                var t = c.ReadOptionalElement<T>(elementName,factory);
                while (t != null)
                {
                    result.Add(t);
                    t = c.
                        ReadOptionalElement<T>(elementName, factory);
                }
                return result;
            }
        }

        public ObservableCollection<T> ReadOptionalCollection<T>(string names, string elementName)
            where T : class, IOfficeXmlSerializable, new()
        {
            PassAttributes();
            if (ReaderLocalName != names)
                return null;
            using (ReadContext c = Read(names))
            {
                var result = new ObservableCollection<T>();
                var t = c.ReadOptionalElement<T>(elementName);
                while (t != null)
                {
                    result.Add(t);
                    t = c.
                        ReadOptionalElement<T>(elementName);
                }
                return result;
            }
        }

        public IEnumerable<T> ReadElements<T>(string names, string elementName)
            where T : class, IOfficeXmlSerializable, new()
        {
            PassAttributes();
            if (ReaderLocalName != names)
                yield break;
            using (ReadContext c = Read(names))
            {
                var t = c.ReadOptionalElement<T>(elementName);
                while (t != null)
                {
                    yield return t;
                    t = c.
                        ReadOptionalElement<T>(elementName);
                }
            }
        }

        public int? GetOptionalInteger(string name)
        {
            string s = GetOptionalString(name);
            if (s != null)
                return int.Parse(s);
            return null;
        }

        public long? GetOptionalInteger64(string name)
        {
            string s = GetOptionalString(name);
            if (s != null)
                return long.Parse(s);
            return null;
        }

        [CLSCompliant(false)]
        public uint? GetOptionalUInteger(string name)
        {
            string s = GetOptionalString(name);
            if (string.IsNullOrEmpty(s))
                return null;
            return uint.Parse(s);
        }

        public string ReadString()
        {
            PassAttributes();
            return _reader.ReadString();
        }

        public int GetInteger(string attributeName)
        {
            string s = GetString(attributeName);
            return int.Parse(s);
        }

        public double GetOptionalDouble(string attributeName, double defaultValue)
        {
            double? result = GetOptionalDouble(attributeName);
            if (result == null)
                return defaultValue;
            return result.Value;
        }


        public Guid? GetOptionalGuid(string name)
        {
            string s = GetOptionalString(name);
            if (s == null)
                return null;
            return new Guid(s);
        }


        public void WarnIf(params string[] names)
        {
            PassAttributes();
            foreach (string name in names)
                if (ReaderLocalName == name)
                    throw new NotImplementedException(name);
        }

        public void Eat(string name)
        {
            PassAttributes();
            if (ReaderLocalName == name)
                _reader.ReadOuterXml();
        }

        public T ReadElement<T>() where T : IOfficeXmlSerializable, new()
        {
            PassAttributes();
            var result = new T();
            try
            {
                result.ReadXml(this);
                return result;
            }
            catch (AlternateContentException)
            {
                var alternateContent = new AlternateContent<T>();
                alternateContent.Read(this, delegate(ReadContext c)
                                                {
                                                    var c2 = c.SwitchNamespace(this._namespaceUri, this.ForceNamespace);
                                                    return c2.ReadElement<T>();
                                                },
                                                 delegate(ReadContext c)
                                                {
                                                    var c2 = c.SwitchNamespace(this._namespaceUri, this.ForceNamespace);
                                                    return c2.ReadElement<T>();
                                                });
                return alternateContent.Item;
            }
        }

        public T ReadElement<T>(ElementFactory<T> factory) where T : IOfficeXmlSerializable
        {
            PassAttributes();
            T result = factory();
            result.ReadXml(this);
            return result;
        }

        public string GetOptionalString(string attributeName, string defaultValue)
        {
            Contract.Requires(defaultValue.IndexOf("urn:schemas") == -1);
            string s = GetOptionalString(attributeName);
            if (s == null)
                return defaultValue;
            return s;
        }

        public CachedPart GetInlinePart(string relationshipId)
        {
            var result = new CachedPart();
            PackageRelationship r = Part.GetRelationship(relationshipId);
            PackagePart part = Part.Package.GetPart(PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri));
            using (Stream stream = part.GetStream())
            {
                result.Data = new byte[stream.Length];
                stream.Read(result.Data, 0, result.Data.Length);
            }
            result.Uri = part.Uri;
            result.ContentType = part.ContentType;
            result.RelationshipType = r.RelationshipType;
            return result;
        }


        [CLSCompliant(false)]
        public uint GetUInteger(string attributeName)
        {
            return uint.Parse(GetString(attributeName));
        }

        public int GetUIntegerAsInteger(string attributeName)
        {
            return (int) uint.Parse(GetString(attributeName));
        }

        public List<T> ReadOptionalWhile<T>(string elementName) where T : class, IOfficeXmlSerializable, new()
        {
            PassAttributes();
            var result = new List<T>();
            var t = ReadOptionalElement<T>(elementName);
            while (t != null)
            {
                result.Add(t);
                t = ReadOptionalElement<T>(elementName);
            }
            return result;
        }

        public T ReadOptionalElementEnumAttribute<T>(string elementName, string attributeName, T defaultValue1,
                                                     T defaultValue2) where T : struct
        {
            PassAttributes();
            moveToContent();
            if (ReaderLocalName != elementName)
                return defaultValue1;
            using (Read(elementName))
            {
                return GetOptionalEnum(attributeName, defaultValue2);
            }
        }

        public bool? ReadOptionalElementNullableBooleanAttribute(string elementName, string attributeName)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return null;
            using (Read(elementName))
            {
                return GetOptionalBoolean(attributeName, true);
            }
        }

        public bool? ReadOptionalElementNullableBooleanAttribute(string elementName)
        {
            return ReadOptionalElementNullableBooleanAttribute(elementName, "val");
        }

        public bool ReadOptionalElementBooleanAttribute(string elementName, string attributeName, bool defaultValue1)
        {
            return ReadOptionalElementBooleanAttribute(elementName, attributeName, defaultValue1, true);
        }

        public bool ReadOptionalElementBooleanAttribute(string elementName, string attributeName, bool defaultValue1,
                                                        bool defaultValue2)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return defaultValue1;
            using (Read(elementName))
            {
                return GetOptionalBoolean(attributeName, defaultValue2);
            }
        }

        public bool? ReadOptionalElementBooleanAttribute(string elementName, string attributeName)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return null;
            using (Read(elementName))
            {
                if (GetOptionalString(attributeName) == null)
                    return true;
                return GetOptionalBoolean(attributeName);
            }
        }

        public T ReadElementAs<T>(string name) where T : IOfficeXmlSerializableAs, new()
        {
            moveToContent();
            PassAttributes();
            var result = new T();
            result.ReadXmlAs(name, this);
            return result;
        }

        public double GetDouble(string attributeName)
        {
            return stringToDouble(GetString(attributeName));
        }

        private static double stringToDouble(string s)
        {
            switch (s)
            {
                case "INF":
                    return double.PositiveInfinity;
            }
            return double.Parse(s, CultureInfo.InvariantCulture);
        }

        [CLSCompliant(false)]
        public uint GetOptionalUInteger(string attributeName, uint defaultValue)
        {
            uint? result = GetOptionalUInteger(attributeName);
            if (result == null)
                return defaultValue;
            return result.Value;
        }

        public bool ReadOptionalElementBooleanAttribute(string elementName, bool defaultValue1, bool defaultValue2)
        {
            return ReadOptionalElementBooleanAttribute(elementName, "val", defaultValue1, defaultValue2);
        }

        public bool ReadOptionalElementBooleanAttribute(string elementName, bool defaultValue1)
        {
            return ReadOptionalElementBooleanAttribute(elementName, "val", defaultValue1, true);
        }

        public int ReadOptionalElementIntegerAttribute(string elementName, int defaultValue)
        {
            return ReadOptionalElementIntegerAttribute(elementName, "val", defaultValue);
        }

        public int? ReadOptionalElementIntegerAttribute(string elementName)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return null;
            using (Read(elementName))
            {
                return GetOptionalInteger("val");
            }
        }

        public int ReadOptionalElementIntegerAttribute(string elementName, string attributeName, int defaultValue)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return defaultValue;
            using (Read(elementName))
            {
                return GetOptionalInteger(attributeName, defaultValue);
            }
        }

        public int ReadElementIntegerAttribute(string elementName)
        {
            return ReadElementIntegerAttribute(elementName, "val");
        }

        [CLSCompliant(false)]
        public uint ReadElementUIntegerAttribute(string elementName)
        {
            return ReadElementUIntegerAttribute(elementName, "val");
        }

        public int ReadElementIntegerAttribute(string elementName, string attributeName)
        {
            PassAttributes();
            using (ReadContext c = Read(elementName))
            {
                return c.GetInteger(attributeName);
            }
        }

        [CLSCompliant(false)]
        public uint ReadElementUIntegerAttribute(string elementName, string attributeName)
        {
            PassAttributes();
            using (ReadContext c = Read(elementName))
            {
                return c.GetUInteger(attributeName);
            }
        }


        public T ReadElementEnumAttribute<T>(string elementName, string attributeName)
        {
            using (ReadContext c = Read(elementName))
            {
                return c.GetEnum<T>(attributeName);
            }
        }

        public double ReadElementDoubleAttribute(string elementName, string attributeName)
        {
            using (ReadContext c = Read(elementName))
            {
                return c.GetDouble(attributeName);
            }
        }

        public bool? ReadOptionalElementBooleanAttribute(string elementName)
        {
            return ReadOptionalElementBooleanAttribute(elementName, "val");
        }

        public ReadContext SwitchNamespace(string namespaceUri, bool forceNamespaceUri)
        {
            var result = new ReadContext(_reader, _part);
            result.copyFrom(this);
            result._namespaceUri = namespaceUri;
            result._data = _data;
            result._forceNamespace = forceNamespaceUri;
            Contract.Assert(result._name != null);
            return result;
        }

        public double ReadOptionalElementDoubleAttribute(string elementName, double defaultValue)
        {
            return ReadOptionalElementDoubleAttribute(elementName, "val", defaultValue);
        }

        public double ReadOptionalElementDoubleAttribute(string elementName, string attributeName, double defaultValue)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return defaultValue;
            using (Read(elementName))
            {
                return GetOptionalDouble(attributeName, defaultValue);
            }
        }

        public double? ReadOptionalElementDoubleAttribute(string elementName)
        {
            return ReadOptionalElementDoubleAttribute(elementName, "val");
        }

        public double? ReadOptionalElementDoubleAttribute(string elementName, string attributeName)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return null;
            using (Read(elementName))
            {
                return GetOptionalDouble(attributeName);
            }
        }

        public int ReadElementIntegerAttribute(string elementName, string attributeName, string namespaceUri)
        {
            PassAttributes();
            using (ReadContext c = Read(elementName))
            {
                return c.GetInteger(attributeName, namespaceUri);
            }
        }

        public int GetInteger(string attributeName, string namespaceUri)
        {
            //\\PassAttributes();
            return int.Parse(_reader[attributeName, namespaceUri]);
        }

        [CLSCompliant(false)]
        public uint ReadOptionalElementUIntegerAttribute(string elementName, string attributeName, uint defaultValue)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return defaultValue;
            using (Read(elementName))
            {
                return GetOptionalUInteger(attributeName, defaultValue);
            }
        }

        [CLSCompliant(false)]
        public uint? ReadOptionalElementUIntegerAttribute(string elementName, string attributeName)
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return null;
            using (Read(elementName))
            {
                return GetOptionalUInteger(attributeName);
            }
        }

        public T? ReadOptionalElementEnumAttribute<T>(string elementName, string attributeName) where T : struct
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return null;
            return ReadElementEnumAttribute<T>(elementName, attributeName);
        }

        public T ReadOptionalElementEnumAttribute<T>(string elementName, T defaultValue) where T : struct
        {
            T? result = ReadOptionalElementEnumAttribute<T>(elementName, "val");
            if (result == null)
                return defaultValue;
            return result.Value;
        }

        public double? GetOptionalDouble(string attributeName)
        {
            string s = GetOptionalString(attributeName);
            if (s == null)
                return null;
            if (s == "NaN") //\\ gen
                return double.NaN;
            return stringToDouble(s);
        }

        public double? GetOptionalDouble(XmlQualifiedName qualifiedName)
        {
            return getOptionalDoubleWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace.NamespaceUri);
        }

        private double? getOptionalDoubleWithNamespace(string attributeName, string namespaceUri)
        {
            string s = _reader[attributeName, namespaceUri];
            if (s == null)
                return null;
            return stringToDouble(s);
        }


        public T? GetOptionalEnum<T>(string attributeName) where T : struct
        {
            string v = GetOptionalString(attributeName);
            if (v == null)
                return null;
            return enumConverter.GetValue<T>(v);
        }

        public ReadContext ReadAndSwitch(string localName, string namespaceUri, bool forceNamespace)
        {
            ReadContext result = Read(localName);
            result._forceNamespace = forceNamespace;
            result._childNamespace = namespaceUri;
            return result;
        }

        public T? ReadOptionalElementEnumAttribute<T>(string elementName) where T : struct
        {
            PassAttributes();
            if (ReaderLocalName != elementName)
                return null;
            return ReadElementEnumAttribute<T>(elementName, "val");
        }

        public void EatIf(string elementName)
        {
            PassAttributes();
            if (ReaderLocalName == elementName)
                _reader.ReadOuterXml();
        }

        private string getString(string name, string namespaceUri)
        {
            return _reader[name, namespaceUri];
        }

        public void EatForNow(string elementName)
        {
            Eat(elementName);
        }

        public string ReadOptionalElementStringAttribute(string elementName, string attributeName)
        {
            moveToContent();
            if (ReaderLocalName != elementName)
                return null;
            using (ReadContext c = Read(elementName))
            {
                return c.GetString(attributeName);
            }
        }

        public float GetFloat(string attributeName)
        {
            return float.Parse(GetString(attributeName));
        }

        public T ReadElementEnumAttribute<T>(string elementName)
        {
            return ReadElementEnumAttribute<T>(elementName, "val");
        }

        [CLSCompliant(false)]
        public uint? ReadOptionalElementUIntegerAttribute(string elementName)
        {
            return ReadOptionalElementUIntegerAttribute(elementName, "val");
        }

        #region Nested type: ReadState

        private enum ReadState
        {
            None,
            Before,
            AfterAttributes,
            AfterAttributesEmpty,
            Disposed,
        }

        #endregion

        public IEnumerable<T> ReadElements<T>(XmlQualifiedName names, string elementName) where T : class, IOfficeXmlSerializable, new()
        {
            PassAttributes();
            if (ReaderLocalName != names.LocalName)
                yield break;
            using (ReadContext c = Read(names))
            {
                var t = c.ReadOptionalElement<T>(elementName);
                while (t != null)
                {
                    yield return t;
                    t = c.
                        ReadOptionalElement<T>(elementName);
                }
            }
        }
    }

    public class AlternateContentException : ApplicationException
    {
    }
}